package io.lvdaxian.mybatis19.builder.xml;

import io.lvdaxian.mybatis19.builder.BaseBuilder;
import io.lvdaxian.mybatis19.builder.MapperBuilderAssistant;
import io.lvdaxian.mybatis19.executor.keygen.Jdbc3KeyGenerator;
import io.lvdaxian.mybatis19.executor.keygen.KeyGenerator;
import io.lvdaxian.mybatis19.executor.keygen.NoKeyGenerator;
import io.lvdaxian.mybatis19.executor.keygen.SelectKeyGenerator;
import io.lvdaxian.mybatis19.mapping.MappedStatement;
import io.lvdaxian.mybatis19.mapping.SqlCommandType;
import io.lvdaxian.mybatis19.mapping.SqlSource;
import io.lvdaxian.mybatis19.scripting.LanguageDriver;
import io.lvdaxian.mybatis19.session.Configuration;
import org.dom4j.Element;

import java.util.List;
import java.util.Locale;

public class XMLStatementBuilder extends BaseBuilder {
  
  // element 元素
  private final Element element;
  // mapper builder 助手
  MapperBuilderAssistant builderAssistant;
  
  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, Element element) {
    super(configuration);
    this.element = element;
    this.builderAssistant = builderAssistant;
  }
  
  /**
   * 从这个方法中 开始解析element 片段
   * <p>
   * //解析语句(select|insert|update|delete)
   * //<select
   * //  id="selectPerson"
   * //  parameterType="int"
   * //  parameterMap="deprecated"
   * //  resultType="hashmap"
   * //  resultMap="personResultMap"
   * //  flushCache="false"
   * //  useCache="true"
   * //  timeout="10000"
   * //  fetchSize="256"
   * //  statementType="PREPARED"
   * //  resultSetType="FORWARD_ONLY">
   * //  SELECT * FROM PERSON WHERE ID = #{id}
   *
   * @author lihh
   */
  public void parseStatementNode() {
    // 拿到 id，就是 select or update 的片段id
    String id = element.attributeValue("id");
    // 参数类型
    String parameterType = element.attributeValue("parameterType");
    Class<?> parameterTypeClass = resolveAlias(parameterType);
    // 外部应用 resultMap
    String resultMap = element.attributeValue("resultMap");
    // 结果类型
    String resultType = element.attributeValue("resultType");
    Class<?> resultTypeClass = resolveAlias(resultType);
    
    // 表示 获取命令名称
    String nodeName = element.getName();
    // 将命令类型 转换为 sql命令类型
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    
    // 解析 字段【flushCache】
    boolean flushCache = false;
    String flushCacheStr = element.attributeValue("flushCache");
    if (null != flushCacheStr)
      flushCache = Boolean.parseBoolean(flushCacheStr);
    boolean useCache = Boolean.parseBoolean(element.attributeValue("useCache", String.valueOf(isSelect)));
    
    // 获取默认的语言驱动器
    Class<?> langClass = configuration.getLanguageRegistry().getDefaultDriverClass();
    LanguageDriver langDriver = configuration.getLanguageRegistry().getDriver(langClass);
    
    // id 表示 片段id
    // parameterTypeClass 表示 参数类
    // langDriver 语言驱动
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // element 这个就是 解析 document 元素
    // configuration 配置文件
    // element 元素内容
    // parameterTypeClass 参数类型 class
    SqlSource sqlSource = langDriver.createSqlSource(configuration, element, parameterTypeClass);
    
    // 属性标记【仅对 insert 有用】, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值 step-14 新增
    String keyProperty = element.attributeValue("keyProperty");
    
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    
    // 由构建者 助手来做这个事情了
    builderAssistant.addMappedStatement(
        id,
        sqlSource,
        sqlCommandType,
        parameterTypeClass,
        resultMap,
        resultTypeClass,
        keyGenerator,
        keyProperty,
        langDriver,
        flushCache,
        useCache
    );
  }
  
  /**
   * 处理 selectKey 的节点
   *
   * @param id                 element 片段id
   * @param parameterTypeClass 表示参数类型
   * @param langDriver         语言驱动
   * @author lihh
   *
   * <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
   * <selectKey keyProperty="id" order="AFTER" resultType="int">
   * SELECT LAST_INSERT_ID()
   * </selectKey>
   * INSERT INTO users (username, email) VALUES (#{username}, #{email})
   * </insert>
   */
  private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
    // 从这里拿到 属性【selectKey】
    List<Element> selectKeyNodes = element.elements("selectKey");
    // 处理 selectKey 节点
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver);
  }
  
  /**
   * 解析 selectKey 节点
   *
   * @param parentId           片段id
   * @param list               selectKey 集合
   * @param parameterTypeClass 参数类
   * @param languageDriver     语言驱动
   * @author lihh
   */
  private void parseSelectKeyNodes(String parentId, List<Element> list, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
    // 依次 处理每个节点
    for (Element nodeToHandle : list) {
      // 结合 id
      String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
      // 继续 解析
      parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, languageDriver);
    }
  }
  
  /**
   * <selectKey keyProperty="id" order="AFTER" resultType="long">
   * SELECT LAST_INSERT_ID()
   * </selectKey>
   */
  private void parseSelectKeyNode(String id, Element nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver) {
    // 返回 类型 字符串
    String resultType = nodeToHandle.attributeValue("resultType");
    // 返回 类型 Class
    Class<?> resultTypeClass = resolveClass(resultType);
    boolean executeBefore = "BEFORE".equals(nodeToHandle.attributeValue("order", "AFTER"));
    // 表示 selectKey 的映射属性 eg：id
    String keyProperty = nodeToHandle.attributeValue("keyProperty");
    
    // default
    String resultMap = null;
    KeyGenerator keyGenerator = new NoKeyGenerator();
    
    // 解析成SqlSource，DynamicSqlSource/RawSqlSource
    SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;
    
    // 调用助手类
    builderAssistant.addMappedStatement(id,
        sqlSource,
        sqlCommandType,
        parameterTypeClass,
        resultMap,
        resultTypeClass,
        keyGenerator,
        keyProperty,
        langDriver, false, false);
    
    // 给id加上namespace前缀
    id = builderAssistant.applyCurrentNamespace(id, false);
    
    // 存放键值生成器配置
    MappedStatement keyStatement = configuration.getMappedStatement(id);
    configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
  }
}
